package protocol

import (
	"fmt"
	"iot/common"
	"iot/common/sys"
	"iot/plat/device"
	"iot/plat/protocol/mymodbus"
	"sync"
)

var (
	cacquisitionInstance *CAcquisitionChannelMgr
	cacquisitionOnce     sync.Once
)

type CAcquisitionChannelMgr struct {
	channels map[string]*CAcquisitionChannel
	chmu     sync.RWMutex
}

func GetChannelMgr() *CAcquisitionChannelMgr {
	cacquisitionOnce.Do(func() {
		cacquisitionInstance = &CAcquisitionChannelMgr{
			channels: make(map[string]*CAcquisitionChannel),
		}
	})
	return cacquisitionInstance
}

func (mgr *CAcquisitionChannelMgr) Start(chidx string) error {
	mgr.chmu.Lock()
	defer mgr.chmu.Unlock()

	info := common.SProtocolInfo{}
	if sys.DbIns().First(&info, "m_index = ?", chidx); sys.DbIns().Error != nil {
		return sys.DbIns().Error
	}

	config := common.SCommunicationConfig{}
	if sys.DbIns().First(&config, "m_index = ?", info.ConfigIndex); sys.DbIns().Error != nil {
		return sys.DbIns().Error
	}

	if mgr.channels[chidx] == nil {
		ch := mgr.NewChannel(info)
		if ch == nil {
			return fmt.Errorf("channel '%s' not found", chidx)
		}
		if err := ch.Init(device.GetDeviceMgr().Device(info.DeviceIndex), config, chidx); err != nil {
			return err
		}
		mgr.channels[chidx] = ch
	}

	if err := mgr.channels[chidx].Stop(); err != nil {
		return err
	}

	if err := mgr.channels[chidx].Run(); err != nil {
		return err
	}

	return nil
}

func (mgr *CAcquisitionChannelMgr) NewChannel(info common.SProtocolInfo) *CAcquisitionChannel {
	ch := &CAcquisitionChannel{
		SProtocolInfo: info,
	}

	switch info.Type {
	case common.EAcquisitionProtocolModbus:
		ch.CProtocol = &mymodbus.CModbus{}
	}
	return ch
}

type CAcquisitionChannel struct {
	common.SProtocolInfo
	CProtocol
}

type CProtocol interface {
	Init(*device.CDevice, common.SCommunicationConfig, string) error
	Connect() error
	Disconnect() error
	Run() error
	Stop() error
}
